package com.luculent.util.extension.datamasking.util;

import com.google.common.base.Charsets;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * 封装各种Hash算法的工具类
 * SHA-1, 安全性较高, 返回byte[](可用Encodes进一步被编码为Hex, Base64)
 * 性能优化，使用ThreadLocal的MessageDigest(from ElasticSearch)
 * 支持带salt并且进行迭代达到更高的安全性.
 *
 * @author zhangyang
 * @date 2020/10/30 10:58
 */
public class HashUtil {


    private static final ThreadLocal<MessageDigest> SHA_1_DIGEST = createThreadLocalMessageDigest("SHA-1");


    private static ThreadLocal<MessageDigest> createThreadLocalMessageDigest(final String digest) {
        // ThreadLocal重用MessageDigest
        return new ThreadLocal<MessageDigest>() {
            @Override
            protected MessageDigest initialValue() {
                try {
                    return MessageDigest.getInstance(digest);
                } catch (NoSuchAlgorithmException e) {
                    throw new RuntimeException(
                            "unexpected exception creating MessageDigest instance for [" + digest + ']', e);
                }
            }
        };
    }

    /**
     * 对输入字符串进行sha1散列.
     *
     * @param input
     * @return
     */
    public static byte[] sha1(byte[] input) {
        return digest(input, get(SHA_1_DIGEST), null, 1);
    }

    /**
     * 对输入字符串进行sha1散列, 编码默认为UTF8.
     *
     * @param input
     * @return
     */
    public static byte[] sha1(String input) {
        return digest(input.getBytes(Charsets.UTF_8), get(SHA_1_DIGEST), null, 1);
    }


    private static MessageDigest get(ThreadLocal<MessageDigest> messageDigest) {
        MessageDigest instance = messageDigest.get();
        instance.reset();
        return instance;
    }

    /**
     * 对字符串进行散列, 支持sha1算法.
     *
     * @param input
     * @param digest
     * @param salt
     * @param iterations
     * @return
     */
    private static byte[] digest(byte[] input, MessageDigest digest, byte[] salt, int iterations) {
        // 带盐
        if (salt != null) {
            digest.update(salt);
        }
        // 第一次散列
        byte[] result = digest.digest(input);
        // 如果迭代次数>1，进一步迭代散列
        for (int i = 1; i < iterations; i++) {
            digest.reset();
            result = digest.digest(result);
        }
        return result;
    }


}
